﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DynORM.Interfaces;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;

namespace DynORM.ReferenceObjects
{
    [XmlRoot(ElementName = Database.DatabaseTag)]
    public class Database : IORMDatabase, IXmlSerializable
    {
        private List<IRowDescriptor> tables = new List<IRowDescriptor>();
        private List<IRow> rows = new List<IRow>();
        private string dbName; //had to relax "readonly" restriction of this member in favor to init logic of xml serializer

        //for serializer
        private Database()
        { }

        public const string DatabaseTag = "Database";
        public const string DatabaseNameAttr = "DatabaseName";
        public const string DatabaseTablesTag = "Tables";
        public const string DatabaseRowsTag = "Rows";

        public Database(string dbName)
        {
            this.dbName = dbName;
        }

        public IEnumerable<IRowDescriptor> Tables
        {
            get { return this.tables; }
        }

        public void AddTable(IRowDescriptor table)
        {
            this.tables.Add(table);
        }

        public void RemoveTable(string tableName)
        {
            IRowDescriptor rowDescriptor = this.tables.FirstOrDefault<IRowDescriptor>(rd => String.Compare(rd.TableName, tableName, StringComparison.OrdinalIgnoreCase) == 0);
            if (rowDescriptor != null)
                this.tables.Remove(rowDescriptor);
        }

        public IEnumerable<IRow> Rows
        {
            get { return this.rows; }
        }

        public void AddRow(IRow row)
        {
            this.rows.Add(row);
        }

        public void RemoveRow(IRow row)
        {
            IPrimaryKey pk = row.PrimaryKey;
            RemoveRow(pk);
        }

        public void RemoveRow(IPrimaryKey pk)
        {
            IRow row = this.rows.FirstOrDefault<IRow>(r => r.PrimaryKey.Equals(pk));
            if (row != null)
                this.rows.Remove(row);
        }

        public string DBName
        {
            get { return this.dbName; }
        }

        //in this collection stored auxiliary data which are needed to create DB (in some RDBMS there is need to provide some additional data)
        //in case of creation Database object from xml this data should be inserted in xml <AuxDatas><AuxData>some string</AuxData></AuxDatas>
        //in cases when Database instanciated not from xml AuxDatas should be added AuxDatas.Add("some string");
        private List<string> auxDatas = new List<string>();
        public List<string> AuxDatas 
        {
            get
            { return this.auxDatas; }
        }

        #region IXmlSerializable
		public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            do
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        {
                            if (string.Compare(reader.Name, DatabaseTag, StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                bool emptyElement = reader.IsEmptyElement;
                                if (reader.HasAttributes)
                                {
                                    while (reader.MoveToNextAttribute())
                                    {
                                        if (string.Compare(reader.Name, DatabaseNameAttr, StringComparison.OrdinalIgnoreCase) == 0)
                                            this.dbName = reader.Value;
                                    }
                                }
                                if (emptyElement)
                                    return;
                            }
                            //else if (string.Compare(reader.Name, DatabaseTablesTag, StringComparison.OrdinalIgnoreCase) == 0)
                            //{  }
                            //else if (string.Compare(reader.Name, DatabaseRowsTag, StringComparison.OrdinalIgnoreCase) == 0)
                            //{  }
                            else if (string.Compare(reader.Name, AuxData.AuxDataTag, StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                this.auxDatas.Add(AuxData.CreateFromXml(reader));
                            }
                            else if (string.Compare(reader.Name, RowDescriptor.RowDescriptorTag, StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                RowDescriptor rd = RowDescriptor.CreateFromXml(reader);
                                this.tables.Add(rd);
                            }
                            else if (string.Compare(reader.Name, Row.RowTag, StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                Row r = Row.CreateFromXml(reader);
                                this.rows.Add(r);
                            }
                        } break;
                    case XmlNodeType.EndElement:
                        {
                            if (string.Compare(reader.Name, DatabaseTag, StringComparison.OrdinalIgnoreCase) == 0)
                                return;
                        } break;
                    default: continue;
                }
            } while (reader.Read());
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteAttributeString(DatabaseNameAttr, this.DBName);

            if (this.AuxDatas.Count > 0)
            {
                writer.WriteStartElement(AuxData.AuxDatasTag);
                foreach(string s in this.AuxDatas)
                    AuxData.WriteToXml(writer, s);
                writer.WriteEndElement();
            }

            if (this.Tables.Count() > 0)
            {
                writer.WriteStartElement(DatabaseTablesTag);
                foreach (RowDescriptor rd in this.Tables)
                {
                    writer.WriteStartElement(RowDescriptor.RowDescriptorTag);
                    rd.WriteXml(writer);
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
            }

            if (this.Rows.Count() > 0)
            {
                writer.WriteStartElement(DatabaseRowsTag);
                foreach (Row r in this.Rows)
                {
                    writer.WriteStartElement(Row.RowTag);
                    r.WriteXml(writer);
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
            }
        }
	    #endregion    
    }
}
